/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun<iot25@lingyun>
 *                  All rights reserved.
 *
 *       Filename:  fifo_chat.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(20/03/25)
 *         Author:  LingYun <iot25@lingyun>
 *      ChangeLog:  1, Release initial version on "20/03/25 14:06:43"
 *                 
 ********************************************************************************/
#include<stdio.h>// 标准输入输出（printf/fgets）
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>
#include<libgen.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>

#define FIFO_FILE1    ".fifo_chat1"
#define FIFO_FILE2    ".fifo_chat2"


int g_stop=0;// 全局退出标志
// SIGPIPE信号处理函数
void sig_pipe(int signum)
{
	if(SIGPIPE == signum )// 检测到管道破裂信号
	{
		printf(" get pipe broken signal and let programe exit\n");
		g_stop;
	}
}

int main(int argc, char **argv)
{
	int 					fdr_fifo;
	int						fdw_fifo;
	int 					rv;
	fd_set					rdset;
	char					buf[1024];
	int 					mode = 0;

	if( argc != 2 )// 参数校验：必须指定模式参数
	{
		printf("Usage: %s [0/1]\n",basename(argv[0]));
		printf("This chat program need run twice,1st time run with [0] and 2nd time with [1]\n");
		return -1; 
	}

	mode = atoi(argv[1]);// 转换模式参数为整数


	if( access(FIFO_FILE1, F_OK))
	{
		printf("FIFO file \"%s\" not exist and create it now\n",FIFO_FILE1);
		mkfifo(FIFO_FILE1,0666);// 创建命名管道（读写权限）

	}
	
	if( access(FIFO_FILE2,F_OK) )
	{
		 printf("FIFO file \"%s\" not exist and create it now\n",FIFO_FILE2);
         mkfifo(FIFO_FILE2,0666);//创建命名管道
	}

	signal(SIGPIPE, sig_pipe);// 注册SIGPIPE信号处理函数
 /* 根据模式打开管道 */
	if( 0 == mode )
	{

		printf(" start open '%s' for read and it will blocked untill write endpoint opend...\n",FIFO_FILE1);
		if( (fdr_fifo=open(FIFO_FILE1,O_RDONLY))<0 )// 阻塞打开FIFO1读端（等待模式1进程打开写端
		{
			printf("Open fifo[%s] for chat write endpoint failure: %s\n",FIFO_FILE1,strerror(errno));
			return -1;
		}
		printf("start open '%s' for read...\n",FIFO_FILE2);
// 打开FIFO2写端
		if( (fdw_fifo=open(FIFO_FILE2,O_WRONLY))<0 )
        {
              printf("Open fifo[%s] for chat write endpoint failure: %s\n",FIFO_FILE2,strerror(errno));
              return -1;
        }
	}
	else
	{
 		/* 这里以只写模式打开命名管道FIFO_FILE1的写端，默认是阻塞模式；如果命名管道的读端被不打开则open()将会一直阻
塞，
 		因为前一个进程是先以读模式打开该管道文件的读端，所以这里必须先以写模式打开该文件的写端，否则会出现死锁 */
 		printf("start open '%s' for write and it will blocked untill read endpoint opened...\n",FIFO_FILE1);
 		
		if( (fdw_fifo=open(FIFO_FILE1, O_WRONLY)) < 0 )
 		{
 			printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
 			return -1;
 		
		}
 		printf("start open '%s' for read...\n", FIFO_FILE2);

 		if( (fdr_fifo=open(FIFO_FILE2, O_RDONLY)) < 0 )
 		{
 			printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
 			return -1;
 		}
 	}

	printf("start chating with another program now, please input message now: \n");

	while( !g_stop )
	{
		FD_ZERO(&rdset);// 清空文件描述符集合
		FD_SET(STDIN_FILENO, &rdset);// 添加标准输入到监听集合
		FD_SET(fdr_fifo,&rdset); // 添加读管道到监听集合

		rv= select(fdr_fifo+1,&rdset,NULL,NULL,NULL);// 使用select多路复用（无限等待）

		if( rv<=0 )// 错误或超时
		{
			printf(" select get timeout or error: %s\n",strerror(errno));
			continue;
		}
 /* 处理管道数据 */
		if( FD_ISSET(fdr_fifo,&rdset) )// 管道有数据可读
		{
			memset(buf, 0, sizeof(buf) );
			rv=read(fdr_fifo,buf,sizeof(buf));

			if(rv<0)
			{
				printf("read data from FIFO get error: %s\n",strerror(errno));
				break;
			}
			else if( 0==rv )// 对方关闭管道
			{
				printf("Another side of FIFO get closed and program will exit now\n");
				break;
			}
			printf("<--%s",buf);// 显示接收的消息


		}
 /* 处理键盘输入 */
		if( FD_ISSET(STDIN_FILENO,&rdset) )// 标准输入有数据
		{
			memset(buf,0, sizeof(buf) );
			fgets(buf, sizeof(buf), stdin);// 读取用户输入
			write(fdw_fifo, buf, strlen(buf));// 写入管道
		}

	}
}

